package com.koalacloud.koala.zhaikucloud.util;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.cat.aliases.AliasesRecord;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Highlight;
import co.elastic.clients.elasticsearch.core.search.Hit;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @Author: GuoYang
 * @Description
 * @create 2022-05-19
 */
@Service
@RequiredArgsConstructor
public class EsQueryOperation {

    private final ElasticsearchClient client;

    /**
     * 查询全部别名
     * @return
     * @throws IOException
     */
    public List<String> aliases() throws IOException {
        List<AliasesRecord>  aliasesRecords = client.cat().aliases().valueBody();
        return aliasesRecords.stream().map(AliasesRecord::alias).collect(Collectors.toList());
    }

    /**
     * 复杂查询
     * Query age = Query.of(q -> q.match(m -> m.field("age").query(22)));
     * Query price = Query.of(q -> q.match(m -> m.field("price").query(22.1)));
     * Query bool = Query.of(q -> q.bool(b -> b.must(age).must(price)));
     * @param query 查询条件
     * @param clazz 对象
     * @return
     * @param <T>
     * @throws IOException
     */
    public <T> List<T> complexQuery(Query query, Class<T> clazz) throws IOException {
        List<T> list = new ArrayList<>();
        String index = clazz.getSimpleName().toLowerCase();
        SearchResponse<T> response = client.search(s -> s
                        .index(index)
                        .query(query), clazz);
        List<Hit<T>> hits = response.hits().hits();
        for (Hit<T> hit : hits) {
            list.add(hit.source());
        }
        return list;
    }

    /**
     * 查询总数
     */
//    public Long count (String indexName){
//        // 指定创建时间
//        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
//        queryBuilder.must(QueryBuilders.termQuery("createTime", 1611378102795L));
//
//        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//        sourceBuilder.query(queryBuilder);
//
//        CountRequest countRequest = new CountRequest(indexName);
//        countRequest.source(sourceBuilder);
//        try {
//            CountResponse countResponse = client.count(countRequest, options);
//            return countResponse.getCount();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//        return 0L;
//    }

    /**
     * 分页查询 from-size
     * 默认:offset从0开始、最大限制数据量10000
     * @param clazz
     * @param offset 偏移量
     * @param size 显示条数
     * @return
     * @param <T>
     * @throws IOException
     */
    public <T> List<T> fromSize(Class<T> clazz, Integer offset, Integer size) throws IOException {
        List<T> list = new ArrayList<>();
        String index = clazz.getSimpleName().toLowerCase();
        SearchResponse<T> search = client.search(s -> s.index(index)
                .query(q -> q.matchAll(m -> m))
                        .from(offset)
                        .size(size), clazz);

        for (Hit<T> hit : search.hits().hits()) {
            list.add(hit.source());
        }
        return list;
    }

    /**
     * searchAfter分页
     * 使用Document中的id排序
     * @param clazz 对象
     * @param fieldName 排序字段
     * @param fieldValue 排序字段值
     * @param size 显示条数
     * @return
     * @param <T>
     */
    public <T> List<T> searchAfter(Class<T> clazz, String fieldName, String fieldValue, Integer size) {
        try {
            List<T> list = new ArrayList<>();
            String index = clazz.getSimpleName().toLowerCase();
            SearchResponse<T> search = client.search(s -> s.index(index)
                            .query(q -> q.matchAll(m -> m))
                            .size(size)
//                            .searchAfter("id", "title")
                            .searchAfter(fieldValue)
                            .sort(o -> o.field(f -> f.field(fieldName).order(SortOrder.Desc)))
                    , clazz);
            for (Hit<T> hit : search.hits().hits()) {
                list.add(hit.source());
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 排序查询
     * @param clazz 对象
     * @param field 排序字段
     * @param size 显示条数
     * @return
     * @param <T>
     */
    public <T> List<T> OrderQuery(Class<T> clazz, String field, Integer size) {
        try {

            List<T> list = new ArrayList<>();
            String index = clazz.getSimpleName().toLowerCase();
            SearchResponse<T> search = client.search(s -> s.index(index)
                            .query(q -> q.matchAll(m -> m))
                            .size(size)
                            .sort(o -> o.field(f -> f.field(field).order(SortOrder.Desc)))
//                            .sort(o -> o.field(f -> f.field("updateTime").order(SortOrder.Asc)))
                    , clazz);

            for (Hit<T> hit : search.hits().hits()) {
                list.add(hit.source());
            }

            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 高亮查询
     * @param query
     * @param clazz
     * @param fields
     * @return
     * @param <T>
     * @throws IOException
     */
    public <T> List<Map<String ,Object>> complexQueryHighlight(Query query, Class<T> clazz, String... fields) throws IOException {
        List<Map<String ,Object>> list =  new ArrayList<Map<String ,Object>>();
        String index = clazz.getSimpleName().toLowerCase();
        Highlight of = Highlight.of(h -> {
                    for (String field : fields) {
                        h.fields(
                                field
                                ,
                                h1 -> h1.preTags("<font color='red'>").postTags("</font>"));
                    }
                    return h;
                }
        );
        SearchResponse<T> response = client.search(s -> s
                        .index(index)
                        .query(query)
                        .highlight(of)
                , clazz
        );
        for (Hit<T> hit : response.hits().hits()) {
            Map<String, Object> map = new HashMap<>();
            map.put("source", hit.source());
            map.put("highlight", hit.highlight());
            list.add(map);
        }

        return list;
    }

    /**
     * 多字段搜索
     * @param clazz 对象
     * @param keyword 关键词
     * @param fields 匹配字段
     * @return
     * @param <T>
     */
    public <T> List<T> multiMatch(Class<T> clazz, String keyword, String... fields) {
        try {
            List<T> list = new ArrayList<>();
            String index = clazz.getSimpleName().toLowerCase();
            SearchResponse<T> search = client.search(s -> s.index(index)
                    .query(q -> q.multiMatch(m -> {
                        for (String field:fields) {
                            m.fields(field).query(keyword);
                        }
                        return m;
                    }))
                    , clazz);

            for (Hit<T> hit : search.hits().hits()) {
                list.add(hit.source());
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 模糊查询
     * @param clazz
     * @param key 关键字
     * @param field 查询字段
     * @return
     * @param <T>
     */
    public <T> List<T> fuzzyQuery(Class<T> clazz, String key, String field) {
        try {
            List<T> list = new ArrayList<>();
            String index = clazz.getSimpleName().toLowerCase();
            SearchResponse<T> search = client.search(s -> s.index(index)
                    .query(q -> q.fuzzy(f -> f.field(field).value(key).fuzziness("2")))
                    , clazz);
            for (Hit<T> hit : search.hits().hits()) {
                list.add(hit.source());
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
